#include "qrender/camera.h"

#include <GL/gl.h>

#include <qopengl.h>

#include <QWidget>
#include <QDebug>
#include <QMouseEvent>
#include <QKeyEvent>
#include <QTimer>
#include <QtMath>

// 屏幕滑动和模型转动的比例系数
#define MODEL_ROTATION_SENSITIVITY 1

Camera::Camera(QObject *parent) : QObject(parent)
{
    initCamera();

    if (auto wd_parent = qobject_cast<QWidget *>(parent)) {
        installRenderForm(wd_parent);
    }

    timer_update_ = new QTimer(this);
    connect(timer_update_, &QTimer::timeout, this, &Camera::updateKeyStatus);
    timer_update_->start(30);
}

Camera::Camera(const Camera &camera)
{
    render_form_ = camera.render_form_;
    last_mouse_pos_ = camera.last_mouse_pos_;
    key_status_ = camera.key_status_;

    camera_eye_ = camera.camera_eye_;
    camera_look_ = camera.camera_look_;
    camera_up_ = camera.camera_up_;

    forward_ = camera.forward_;
    right_ = camera.right_;
    up_ = camera.up_;

    original_camera_eye_ = camera.original_camera_eye_;
    original_camera_look_ = camera.original_camera_look_;
    original_camera_up_ = camera.original_camera_up_;

    view_matrix_ = camera.view_matrix_;
    gravity_matrix_ = camera.gravity_matrix_;
    projection_matrix_ = camera.projection_matrix_;

    rotate_angle_ = camera.rotate_angle_;
    rotate_enable_ = camera.rotate_enable_;

    mouse_scale_factor_ = camera.mouse_scale_factor_;

    timer_update_ = new QTimer(this);
    connect(timer_update_, &QTimer::timeout, this, &Camera::updateKeyStatus);
    timer_update_->start(30);
}

void Camera::installRenderForm(QWidget *render_form)
{
    // 取消上一次绑定的窗口
    if (render_form_) {
        if (render_form_ == render_form) {
            return;
        }

        render_form_->removeEventFilter(this);
    }

    // 更新窗口指针
    render_form_ = render_form;

    // 绑定新窗口
    if (render_form_) {
        render_form_->installEventFilter(this);
    }
}

void Camera::setCamera(const QVector3D &eye, const QVector3D &look,
                       const QVector3D &up)
{
    rotate_angle_ = { 0.0, 0.0, 0.0 };

    original_camera_eye_ = eye;
    original_camera_look_ = look;
    original_camera_up_ = up;

    camera_eye_ = original_camera_eye_ * gravity_matrix_;
    camera_look_ = original_camera_look_ * gravity_matrix_;
    camera_up_ = original_camera_up_ * gravity_matrix_;

    forward_ = camera_eye_ - camera_look_;
    right_ = QVector3D::crossProduct(camera_up_, forward_);
    up_ = QVector3D::crossProduct(forward_, right_);

    forward_.normalize();
    right_.normalize();
    up_.normalize();

    // 更新视图坐标系
    updateViewMatrix();
}

void Camera::setRotateEnable(QVector3D rotate_enable)
{
    rotate_enable_ = rotate_enable;
}

void Camera::setRotateEnableX(bool enable)
{
    rotate_enable_.setX(enable);
}

void Camera::setRotateEnableY(bool enable)
{
    rotate_enable_.setY(enable);
}

void Camera::setRotateEnableZ(bool enable)
{
    rotate_enable_.setZ(enable);
}

QMatrix4x4 Camera::getViewMatrix() const
{
    return view_matrix_;
}

QVector3D Camera::getViewPosition() const
{
    return camera_eye_;
}

QVector3D Camera::getRotateAngle() const
{
    return rotate_angle_;
}

void Camera::rotate(float angle, const QVector3D &axis)
{
    // 三个方向都不能转
    if (!rotate_enable_.x() && !rotate_enable_.y() && !rotate_enable_.z()) {
        return;
    }

    // 三个方向旋转角度为 0
    if (angle == 0 || (axis.x() == 0 && axis.y() == 0 && axis.z() == 0)) {
        return;
    }

    auto distance = getDistance();

    translate(distance);

#if 0
    float slide_d =
        MODEL_ROTATION_SENSITIVITY * qDegreesToRadians(angle); // 弧度
    rotate_angle_.setX(normalizeAngle(rotate_angle_.x() + slide_d * axis.x()));
    rotate_angle_.setY(normalizeAngle(rotate_angle_.y() + slide_d * axis.y()));
    rotate_angle_.setZ(normalizeAngle(rotate_angle_.z() + slide_d * axis.z()));
    view_matrix_.rotate(slide_d, axis);
#else
    float slide_d =
        MODEL_ROTATION_SENSITIVITY * qDegreesToRadians(angle); // 弧度
    float cs = cos(slide_d);
    float sn = sin(slide_d);

    // x 轴旋转
    if (rotate_enable_.x()) {
        rotate_angle_.setX(normalizeAngle(
            rotate_angle_.x() + MODEL_ROTATION_SENSITIVITY * angle * axis.x()));

        if (axis.x()) {
            QVector3D t(up_);
            QVector3D s(forward_);

            up_ = cs * t - sn * s;
            forward_ = sn * t + cs * s;
        }
    }

    // y 轴旋转
    if (rotate_enable_.y()) {
        rotate_angle_.setY(normalizeAngle(
            rotate_angle_.y() + MODEL_ROTATION_SENSITIVITY * angle * axis.y()));

        if (axis.y()) {
            QVector3D t(forward_);
            QVector3D s(right_);

            forward_ = cs * t - sn * s;
            right_ = sn * t + cs * s;
        }
    }

    // z 轴旋转
    if (rotate_enable_.z()) {
        rotate_angle_.setZ(normalizeAngle(
            rotate_angle_.z() + MODEL_ROTATION_SENSITIVITY * angle * axis.z()));
        if (axis.z()) {
            QVector3D t(right_);
            QVector3D s(up_);

            right_ = cs * t - sn * s;
            up_ = sn * t + cs * s;
        }
    }

#endif

    translate(-distance);
}

void Camera::rotate(float angle_x, float angle_y, float angle_z)
{
    if (angle_x) {
        rotate(angle_x, { 1.0, 0.0, 0.0 });
    }
    if (angle_y) {
        rotate(angle_y, { 0.0, 1.0, 0.0 });
    }
    if (angle_z) {
        rotate(angle_z, { 0.0, 0.0, 1.0 });
    }
}

void Camera::translate(float x, float y, float z)
{
    camera_eye_ -= x * right_ + y * up_ + z * forward_;

    updateViewMatrix();

    //    view_matrix_.translate(translation);
}

void Camera::setMouseScaleFactor(float scale_factor)
{
    mouse_scale_factor_ = scale_factor;
}

void Camera::setProjectionMatrix(float vertical_angle, float aspect_ratio,
                                 float near_plane, float far_plane)
{
    projection_matrix_.setToIdentity();
    projection_matrix_.perspective(vertical_angle, aspect_ratio, near_plane,
                                   far_plane);
}

QMatrix4x4 Camera::getProjectionMatrix() const
{
    return projection_matrix_;
}

QMatrix4x4 Camera::getGravityMatrix() const
{
    return gravity_matrix_;
}

void Camera::setGravityComponent(const QVector3D &gravity_component)
{
    gravity_matrix_.setToIdentity();

    // 转换为单位向量
    QVector3D component(gravity_component);
    component.normalize();

    // 如果与世界坐标系的重力方向一致则不计算
    if (0 != component.x() || 0 != component.y() || -1 != component.z()) {
        // z 轴方向为重力的反方向，根据世界坐标系中的 x, y 取重力坐标系的 x, y
        QVector3D x, y, z = -component;
        if (z != QVector3D(1.0, 0.0, 0.0)) {
            x = QVector3D(1.0, 0.0, 0.0);
        } else {
            x = QVector3D(0.0, 1.0, 0.0);
        }
        y = QVector3D::crossProduct(z, x).normalized();
        x = QVector3D::crossProduct(z, y);
        qDebug() << __FUNCTION__ << x << y << z << gravity_component;

        gravity_matrix_.setRow(0, x);
        gravity_matrix_.setRow(1, y);
        gravity_matrix_.setRow(2, z);
        gravity_matrix_.setRow(3, { 0, 0, 0, 1 });
    }

    setCamera(original_camera_eye_, original_camera_look_, original_camera_up_);
}

QVector3D Camera::getDistance()
{
    // 计算单位向量
    return QVector3D(QVector3D::dotProduct(camera_eye_, right_),
                     QVector3D::dotProduct(camera_eye_, up_),
                     QVector3D::dotProduct(camera_eye_, forward_));
}

CameraInfo Camera::getInfo()
{
    CameraInfo info;
    info.view_matrix = getViewMatrix();
    info.view_position = getViewPosition();

    info.rotate_angle = getRotateAngle();

    info.projection_matrix = getProjectionMatrix();
    info.gravity_matrix = getGravityMatrix();

    info.distance = getDistance();

    info.camera_eye = camera_eye_;
    info.camera_look = camera_look_;
    info.camera_up = camera_up_;

    info.forward = forward_;
    info.right = right_;
    info.up = up_;

    info.valid = true;
    return info;
}

bool Camera::eventFilter(QObject *watched, QEvent *event)
{
    if (render_form_ && watched == render_form_) {
        // 鼠标操作
        if (auto mouse_event = dynamic_cast<QMouseEvent *>(event)) {
            if (mouse_event->type() == QMouseEvent::MouseButtonPress) {
                last_mouse_pos_ = mouse_event->pos();
            }

            if (mouse_event->type() == QMouseEvent::MouseMove) {
                float dx = mouse_event->x() - last_mouse_pos_.x();
                float dy = mouse_event->y() - last_mouse_pos_.y();

                // 同时按下时，优先旋转，不移动
                // 如果锁定鼠标，则只旋转，移动交给按键
                // 左键旋转
                if (render_form_->hasMouseTracking() ||
                    mouse_event->buttons() & Qt::LeftButton) {
                    rotate(0, dx, 0); // 横向移动绕着 Y 轴旋转
                }
                // 右键移动
                else if (mouse_event->buttons() & Qt::RightButton) {
                    translate(
                        { dx, -dy, 0 }); // 视图坐标系 y 轴向上，与屏幕相反
                }

                last_mouse_pos_ = mouse_event->pos();

                if (render_form_->hasMouseTracking()) {
                    centerMouseInWidget();
                }

                // 拦截鼠标左键拖拽移动窗口功能
                if (mouse_event->buttons() & Qt::LeftButton) {
                    return true;
                }
            }
        }

        // 滚轮操作
        if (auto wheel_event = dynamic_cast<QWheelEvent *>(event)) {
            if (wheel_event->type() == QWheelEvent::Wheel) {
                scale(wheel_event->angleDelta().y() / 120.0 *
                      mouse_scale_factor_);
            }
        }

        // 按键操作
        if (auto key_event = dynamic_cast<QKeyEvent *>(event)) {
            if (key_event->type() == QKeyEvent::KeyPress) {
                switch (key_event->key()) {
                case Qt::Key_W:
                    key_status_ |= KeyStatus_Forword;
                    break;
                case Qt::Key_S:
                    key_status_ |= KeyStatus_Back;
                    break;
                case Qt::Key_A:
                case Qt::Key_Left:
                    key_status_ |= KeyStatus_Left;
                    break;
                case Qt::Key_D:
                case Qt::Key_Right:
                    key_status_ |= KeyStatus_Right;
                    break;
                case Qt::Key_Control:
                case Qt::Key_Down:
                    key_status_ |= KeyStatus_Down;
                    break;
                case Qt::Key_Space:
                case Qt::Key_Up:
                    key_status_ |= KeyStatus_Up;
                    break;
                case Qt::Key_Q:
                    key_status_ |= KeyStatus_TurnLeft;
                    break;
                case Qt::Key_E:
                    key_status_ |= KeyStatus_TurnRight;
                    break;
                case Qt::Key_Enter:
                case Qt::Key_Return:
                    render_form_->setMouseTracking(true);
                    render_form_->setCursor(QCursor(Qt::BlankCursor));
                    break;
                case Qt::Key_Escape:
                    render_form_->setMouseTracking(false);
                    render_form_->setCursor(QCursor(Qt::ArrowCursor));
                    break;
                }
            }

            if (key_event->type() == QKeyEvent::KeyRelease) {
                switch (key_event->key()) {
                case Qt::Key_W:
                    key_status_ &= ~KeyStatus_Forword;
                    break;
                case Qt::Key_S:
                    key_status_ &= ~KeyStatus_Back;
                    break;
                case Qt::Key_A:
                case Qt::Key_Left:
                    key_status_ &= ~KeyStatus_Left;
                    break;
                case Qt::Key_D:
                case Qt::Key_Right:
                    key_status_ &= ~KeyStatus_Right;
                    break;
                case Qt::Key_Control:
                case Qt::Key_Down:
                    key_status_ &= ~KeyStatus_Down;
                    break;
                case Qt::Key_Space:
                case Qt::Key_Up:
                    key_status_ &= ~KeyStatus_Up;
                    break;
                case Qt::Key_Q:
                    key_status_ &= ~KeyStatus_TurnLeft;
                    break;
                case Qt::Key_E:
                    key_status_ &= ~KeyStatus_TurnRight;
                    break;
                }
            }

            if (auto resize_event = dynamic_cast<QResizeEvent *>(event)) {
                if (resize_event->type() == QResizeEvent::Resize) {
                    setProjectionMatrix(45.0f,
                                        (GLdouble)render_form_->width() /
                                            (GLdouble)render_form_->height(),
                                        0.01f, 6000.0f);
                }
            }
        }
    }

    return QObject::eventFilter(watched, event);
}

void Camera::initCamera()
{
    rotate_angle_ = { 0, 0, 0 };

    setCamera({ 0.0, 0.0, 1.0 }, { 0.0, 0.0, 0.0 }, { 0.0, 0.0, 1.0 });
}

double Camera::normalizeAngle(double angle)
{
    if (angle > 360) {
        angle -= 360.0;
    } else if (angle < -360) {
        angle += 360.0;
    } else {
        return angle;
    }

    return normalizeAngle(angle);
}

QMatrix4x4 Camera::adjoint(const QMatrix4x4 &mat)
{
    QMatrix4x4 adj;
    adj.setColumn(0, QVector3D::crossProduct(mat.row(1).toVector3D(),
                                             mat.row(2).toVector3D()));
    adj.setColumn(1, QVector3D::crossProduct(mat.row(2).toVector3D(),
                                             mat.row(0).toVector3D()));
    adj.setColumn(2, QVector3D::crossProduct(mat.row(0).toVector3D(),
                                             mat.row(1).toVector3D()));
    adj.setColumn(
        3, QVector4D(0.0f, 0.0f, 0.0f, 1.0f)); // Assuming it's a 4x4 matrix

    // Transpose the adjoint matrix to get the adjugate matrix
    adj = adj.transposed();
    return adj;
}

void Camera::updateViewMatrix()
{
    view_matrix_.setRow(
        0, QVector4D(right_, -QVector3D::dotProduct(camera_eye_, right_)));
    view_matrix_.setRow(
        1, QVector4D(up_, -QVector3D::dotProduct(camera_eye_, up_)));
    view_matrix_.setRow(
        2, QVector4D(forward_, -QVector3D::dotProduct(camera_eye_, forward_)));
    view_matrix_.setRow(3, QVector4D(0.0, 0.0, 0.0, 1.0));
}

void Camera::centerMouseInWidget()
{
    if (!render_form_) {
        return;
    }

    QPoint center(render_form_->width() / 2, render_form_->height() / 2);
    QPoint globalCenter = render_form_->mapToGlobal(center);
    QCursor::setPos(globalCenter); // FIXME: 不知道为什么无效
    last_mouse_pos_ = center;
}

void Camera::updateKeyStatus()
{
    if (key_status_ & KeyStatus_Up) {
        translate({ 0, 5, 0 });
    }

    if (key_status_ & KeyStatus_Down) {
        translate({ 0, -5, 0 });
    }

    if (key_status_ & KeyStatus_Left) {
        translate({ -5, 0, 0 });
    }

    if (key_status_ & KeyStatus_Right) {
        translate({ 5, 0, 0 });
    }

    if (key_status_ & KeyStatus_Forword) {
        translate({ 0, 0, 5 });
    }

    if (key_status_ & KeyStatus_Back) {
        translate({ 0, 0, -5 });
    }

    if (key_status_ & KeyStatus_TurnLeft) {
        rotateY(-1);
    }

    if (key_status_ & KeyStatus_TurnRight) {
        rotateY(1);
    }
}
